/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
* Sleepycat Software. All rights reserved.
*
* $Id: Node.java,v 1.1 2006/05/06 09:00:15 ckaestne Exp $
*/
package com.sleepycat.je.tree;
import java.nio.ByteBuffer;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.cleaner.UtilizationTracker;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.INList;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.LogException;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LogWritable;
import com.sleepycat.je.log.LoggableObject;
/**
* A Node contains all the common base information for any JE B-Tree node.
*/
public abstract class Node
implements LoggableObject, LogReadable, LogWritable {
/*
* The last allocated id. Note that nodeids will be shared
* across db environments. lastAllocatedId must be initialized at
* startup by Recovery.
*/
public synchronized static void setLastNodeId(long id) {
if (lastAllocatedId < id) {
lastAllocatedId = id;
}
}
private static long lastAllocatedId = 0;
private static final String BEGIN_TAG = "<node>";
private static final String END_TAG = "</node>";
// The unique id of this node
private long nodeId;
/**
* Disallow use
*/
private Node() {
}
/**
* Create a new node, assigning it the next available node id.
*/
protected Node(boolean init) {
if (init) {
nodeId = getNextNodeId();
}
}
/**
* Increment and return the next usable id. Must be synchronized.
*/
public static synchronized long getNextNodeId() {
return ++lastAllocatedId;
}
/**
* Get the latest id, for checkpointing.
*/
public static synchronized long getLastId() {
return lastAllocatedId;
}
/**
* Initialize a node that has been faulted in from the log
*/
public void postFetchInit(DatabaseImpl db, long sourceLsn)
throws DatabaseException {
/* Nothing to do. */
}
public long getNodeId() {
return nodeId;
}
/* For unit tests only. */
void setNodeId(long nid) {
nodeId = nid;
}
public void verify(byte[] maxKey)
throws DatabaseException {
}
/**
* @return true if this node is a duplicate-bearing node type, false
* if otherwise.
*/
public boolean containsDuplicates() {
return false;
}
/**
* Cover for LN's and just return 0 since they'll always be at the bottom
* of the tree.
*/
int getLevel() {
return 0;
}
/*
* Depth first search through a duplicate tree looking for an LN that
* has nodeId. When we find it, set location.bin and index and return
* true. If we don't find it, return false.
*
* No latching is performed.
*/
boolean matchLNByNodeId(TreeLocation location, long nodeId)
throws DatabaseException {
throw new DatabaseException("matchLNByNodeId called on non DIN/DBIN");
}
/**
* Add yourself to the in memory list if you're a type of node that
* should belong.
*/
abstract void rebuildINList(INList inList)
throws DatabaseException;
/**
* Remove yourself from the in memory list if you're a type of node that
* is put there.
*/
abstract void accountForSubtreeRemoval(INList inList,
UtilizationTracker tracker)
throws DatabaseException;
/**
* @return true if you're part of a deletable subtree.
*/
abstract boolean isValidForDelete()
throws DatabaseException;
/**
* @return true if you're an IN in the search path
*/
abstract protected boolean isSoughtNode(long nid, boolean updateGeneration)
throws DatabaseException;
/**
* @return true if you can be the ancestor of the target IN.
* Currently the determining factor is whether the target IN contains
* duplicates.
*/
abstract protected boolean canBeAncestor(boolean targetContainsDuplicates);
/**
* Return the approximate size of this node in memory, if this
* size should be included in it's parents memory accounting.
* For example, all INs return 0, because they are accounted for
* individually. LNs must return a count, they're not counted on
* the INList.
*/
protected long getMemorySizeIncludedByParent() {
return 0;
}
/*
* Dumping
*/
/**
* Default toString method at the root of the tree.
*/
public String toString() {
return this.dumpString(0, true);
}
private String beginTag() {
return BEGIN_TAG;
}
private String endTag() {
return END_TAG;
}
public void dump(int nSpaces) {
System.out.print(dumpString(nSpaces, true));
}
String dumpString(int nSpaces, boolean dumpTags) {
StringBuffer self = new StringBuffer();
self.append(TreeUtils.indent(nSpaces));
if (dumpTags) {
self.append(beginTag());
}
self.append(nodeId);
if (dumpTags) {
self.append(endTag());
}
return self.toString();
}
public String shortDescription() {
return "<" + getType() + "/" + getNodeId();
}
public String getType() {
return getClass().getName();
}
/*
* Logging support
*/
/**
* @see LoggableObject#getLogType
*/
public abstract LogEntryType getLogType();
/**
* @see LoggableObject#marshallOutsideWriteLatch
* By default, nodes can be marshalled outside the log write latch.
*/
public boolean marshallOutsideWriteLatch() {
return true;
}
/**
* @see LoggableObject#countAsObsoleteWhenLogged
*/
public boolean countAsObsoleteWhenLogged() {
return false;
}
/**
* @see LoggableObject#postLogWork
*/
public void postLogWork(long justLoggedLsn)
throws DatabaseException {
}
/**
* @see LoggableObject#getLogSize
*/
public int getLogSize() {
return LogUtils.LONG_BYTES;
}
/**
* @see LogWritable#writeToLog
*/
public void writeToLog(ByteBuffer logBuffer) {
LogUtils.writeLong(logBuffer, nodeId);
}
/**
* @see LogReadable#readFromLog
*/
public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
throws LogException {
nodeId = LogUtils.readLong(itemBuffer);
}
/**
* @see LogReadable#dumpLog
*/
public void dumpLog(StringBuffer sb, boolean verbose) {
sb.append(BEGIN_TAG);
sb.append(nodeId);
sb.append(END_TAG);
}
}